package nl.unimaas.micc.umsnooker.domain;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import com.jme.math.FastMath;
import com.jme.math.Vector3f;

public class Table extends GameObject
{
	private float width;
	private float length;

	private float baulkLineDistance;
	private float dRadius;
	private float blackDistance;

	private Vector3f brownSpot;
	private Vector3f yellowSpot;
	private Vector3f greenSpot;
	private Vector3f blueSpot;
	private Vector3f pinkSpot;
	private Vector3f blackSpot;
	private List<Vector3f> redSpots;
	
	private Map<BilliardBall.Type, Vector3f> colorSpots;
	
	private List<Cushion> cushions;

	private float pocketRadius;
	private List<Pocket> pockets;
	
	private float pocketCornerRadius;
	private List<PocketCorner> pocketCorners;

	private float ballRadius;

	private float slidingFriction;
	private float rollingFriction;
	private float spinningFriction;

	public Table(float width, float length, float baulkLineDistance,
			float dRadius, float blackDistance, float ballRadius, float pocketRadius, float pocketCornerRadius)
	{
		this.width = width;
		this.length = length;
		this.baulkLineDistance = baulkLineDistance;
		this.dRadius = dRadius;
		this.blackDistance = blackDistance;
		this.ballRadius = ballRadius;
		this.pocketRadius = pocketRadius;
		this.pocketCornerRadius = pocketCornerRadius;

		
		// Standard initial ball positions
		brownSpot = new Vector3f(width / 2f, baulkLineDistance, 0);
		yellowSpot = new Vector3f(brownSpot.x + dRadius, brownSpot.y, 0);
		greenSpot = new Vector3f(brownSpot.x - dRadius, brownSpot.y, 0);
		blueSpot = new Vector3f(width / 2f, length / 2f, 0);
		pinkSpot = new Vector3f(width / 2f, length * (3f / 4f), 0);
		blackSpot = new Vector3f(width / 2f, length - blackDistance, 0);
		
		colorSpots = new HashMap<BilliardBall.Type, Vector3f>();
		colorSpots.put(BilliardBall.Type.YELLOW, yellowSpot);
		colorSpots.put(BilliardBall.Type.GREEN, greenSpot);
		colorSpots.put(BilliardBall.Type.BROWN, brownSpot);
		colorSpots.put(BilliardBall.Type.BLUE, blueSpot);
		colorSpots.put(BilliardBall.Type.PINK, pinkSpot);
		colorSpots.put(BilliardBall.Type.BLACK, blackSpot);


		float spacing = 0.00001f;
		
		redSpots = new ArrayList<Vector3f>(15);
		for (int y = 0; y < 5; y++)
		{
			for (int x = -y; x <= y; x += 2)
			{
				float entropy = 0;
				//float entropy = generator.nextFloat() / 10000;
				redSpots.add(new Vector3f(pinkSpot.x + x * (ballRadius+spacing+entropy),
						pinkSpot.y + 2f * (ballRadius+spacing+entropy) + y * FastMath.sqrt(3)
								* (ballRadius+spacing+entropy), 0));

			}
		}

//		for (int y = 0; y < 20; y++)
//		{
//			for (int x = -10; x <= 10; x += 2)
//			{
////					float entropy = 0;
//				float entropy = generator.nextFloat() / 10000;
//				redSpots.add(new Vector3f(blueSpot.x + x * (ballRadius*2+spacing+entropy),
//						blueSpot.y + 2f * (ballRadius+spacing+entropy) + y
//								* (ballRadius*2+spacing+entropy), 0));
//
//			}
//		}


		
		pockets = new ArrayList<Pocket>(6);
		
		pockets.add(new Pocket(new Vector3f(-pocketRadius/FastMath.sqrt(2), -pocketRadius/FastMath.sqrt(2), 0), pocketRadius));
		pockets.add(new Pocket(new Vector3f(width + pocketRadius/FastMath.sqrt(2), -pocketRadius/FastMath.sqrt(2), 0), pocketRadius));
		pockets.add(new Pocket(new Vector3f(-pocketRadius/FastMath.sqrt(2), length + pocketRadius/FastMath.sqrt(2), 0), pocketRadius));
		pockets.add(new Pocket(new Vector3f(width + pocketRadius/FastMath.sqrt(2), length + pocketRadius/FastMath.sqrt(2), 0), pocketRadius));
		
		pockets.add(new Pocket(new Vector3f(-pocketRadius, length/2f, 0), pocketRadius));
		pockets.add(new Pocket(new Vector3f(width + pocketRadius, length/2f, 0), pocketRadius));
		
		
		pocketCorners = new ArrayList<PocketCorner>(12);
		
		float cornerPocketOffset = FastMath.sqrt(2)*pocketRadius + (FastMath.sqrt(2)-1)*pocketCornerRadius;
		
		for (int x : new int[]{-1, 1})
		{
			for (int y : new int[]{-1, 1})
			{
				pocketCorners.add(new PocketCorner(new Vector3f(
										width/2f + x*(width/2f - cornerPocketOffset),
										length/2f + y*(length/2f + pocketCornerRadius),
										0		
								), pocketCornerRadius));
				pocketCorners.add(
						new PocketCorner(
								new Vector3f(
										width/2f + x*(width/2f + pocketCornerRadius),
										length/2f + y*(length/2f - cornerPocketOffset),
										0
								),
								pocketCornerRadius
						)
				);
			}
		}
		
		pocketCorners.add(new PocketCorner(new Vector3f(-pocketCornerRadius, length/2f - pocketRadius - pocketCornerRadius, 0), pocketCornerRadius));
		pocketCorners.add(new PocketCorner(new Vector3f(-pocketCornerRadius, length/2f + pocketRadius + pocketCornerRadius, 0), pocketCornerRadius));
		
		pocketCorners.add(new PocketCorner(new Vector3f(width + pocketCornerRadius, length/2f - pocketRadius - pocketCornerRadius, 0), pocketCornerRadius));
		pocketCorners.add(new PocketCorner(new Vector3f(width + pocketCornerRadius, length/2f + pocketRadius + pocketCornerRadius, 0), pocketCornerRadius));
		
		cushions = new ArrayList<Cushion>(6);
		
		float vCushionLength = 0.5f*length - cornerPocketOffset-pocketRadius-pocketCornerRadius;
		float hCushionLength = width - 2f*cornerPocketOffset;

		// West
		cushions.add(new Cushion(new Vector3f(0,cornerPocketOffset+0.5f*vCushionLength,0), vCushionLength, Cushion.Orientation.WEST));
		cushions.add(new Cushion(new Vector3f(0,length-cornerPocketOffset-0.5f*vCushionLength,0), vCushionLength, Cushion.Orientation.WEST));
		
		// East
		cushions.add(new Cushion(new Vector3f(width,cornerPocketOffset+0.5f*vCushionLength,0), vCushionLength, Cushion.Orientation.EAST));
		cushions.add(new Cushion(new Vector3f(width,length-cornerPocketOffset-0.5f*vCushionLength,0), vCushionLength, Cushion.Orientation.EAST));
		
		// South
		cushions.add(new Cushion(new Vector3f(cornerPocketOffset+0.5f*hCushionLength,0,0), vCushionLength, Cushion.Orientation.SOUTH));
		
		// North
		cushions.add(new Cushion(new Vector3f(cornerPocketOffset+0.5f*hCushionLength,length,0), vCushionLength, Cushion.Orientation.NORTH));

	}
	
	public Vector3f getInitialSpot(BilliardBall.Type type)
	{
		if (!colorSpots.containsKey(type))
		{
			throw new RuntimeException("No initial spot for type "+type);
		}
		return colorSpots.get(type);
	}

	public float getWidth()
	{
		return width;
	}

	public float getLength()
	{
		return length;
	}

	public float getBaulkLineDistance()
	{
		return baulkLineDistance;
	}

	public float getDRadius()
	{
		return dRadius;
	}

	public float getBlackDistance()
	{
		return blackDistance;
	}

	public Vector3f getBrownSpot()
	{
		return brownSpot;
	}

	public Vector3f getYellowSpot()
	{
		return yellowSpot;
	}

	public Vector3f getGreenSpot()
	{
		return greenSpot;
	}

	public Vector3f getBlueSpot()
	{
		return blueSpot;
	}

	public Vector3f getPinkSpot()
	{
		return pinkSpot;
	}

	public Vector3f getBlackSpot()
	{
		return blackSpot;
	}

	public List<Vector3f> getRedSpots()
	{
		return redSpots;
	}
	
	public List<Cushion> getCushions()
	{
		return cushions;
	}

	public float getPocketRadius()
	{
		return pocketRadius;
	}

	public List<Pocket> getPockets()
	{
		return pockets;
	}

	public float getPocketCornerRadius()
	{
		return pocketCornerRadius;
	}

	public List<PocketCorner> getPocketCorners()
	{
		return pocketCorners;
	}
	
	public float getBallRadius()
	{
		return ballRadius;
	}

	public float getSlidingFriction()
	{
		return slidingFriction;
	}

	public float getRollingFriction()
	{
		return rollingFriction;
	}

	public float getSpinningFriction()
	{
		return spinningFriction;
	}

}
